package cn.chendd.service;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static cn.chendd.common.CommonConstants.*;
import cn.chendd.common.CommonConstants;
import cn.chendd.enums.EnumGameStatus;
import cn.chendd.enums.EnumLineScore;
import cn.chendd.vo.GameContext;

/**
 * 游戏业务处理
 * @author chendd
 *
 */
public class GameService{

	private GameContext gameContext;//上下文数据
	private Random random = new Random();//随即方块的编号
	
	public GameService(GameContext gameContext){
		this.gameContext = gameContext;
	}
	
	/**
	 * 根据方块类型获取一个新的方块
	 * @param code 方块编号
	 * @return 方块的4个格子的下标
	 */
	public Point[] getPointsByCode(int code){
		Point ps[] = new Point[4];
		Point points[] = CommonConstants.TYPE_CONFIG.get(code);
		for(int i=0 ; i < points.length ; i++){
			Point point = points[i];
			Point p = new Point(point.x , point.y);
			ps[i] = p;
		}
		return ps;
	}

	/**
	 * 响应键盘上事件，方块旋转
	 */
	public synchronized void keyUp() {
		if(EnumGameStatus.RUNING.getStatus() != this.gameContext.getGameStatus()){
			return;
		}
		//旋转方块
		this.round();
	}
	
	/**
	 * 响应方块的下落事件，下移一格
	 */
	public synchronized void keyDown() {
		if(EnumGameStatus.RUNING.getStatus() != this.gameContext.getGameStatus()){
			return;
		}
		boolean canMove = this.move(0, 1);
		Point points[] = this.gameContext.getActPoint();
		if(canMove){
			//如果可以移动，则移动
			//绘制地图阴影
			return;
		}
		//刷新新方块时，判定游戏是否结束，新方块的坐标值为true，则为游戏结束
		boolean gameMap[][] = this.gameContext.getGameMap();
		//如果不能移动则堆积方块
		for (Point point : points) {
			gameMap[point.x][point.y] = true;
		}

		int next = this.gameContext.getNext();
		Point newPoints[] = this.getPointsByCode(next);
		int nextNext = random.nextInt(7);
		nextBlock(newPoints , nextNext);//下一个方块和下下个方块的序号
		
		clearLine();//判定是否有消行，并且记录等级与分数
		
		checkGameOver(gameMap , newPoints);//检查游戏是否结束
		
	}

	/**
	 * 响应键盘事件，向左移动一个格子
	 */
	public synchronized void keyLeft() {
		if(EnumGameStatus.RUNING.getStatus() != this.gameContext.getGameStatus()){
			return;
		}
		this.move(-1, 0);
	}

	/**
	 * 响应键盘事件，向右移动一个格子
	 */
	public synchronized void keyRight() {
		if(EnumGameStatus.RUNING.getStatus() != this.gameContext.getGameStatus()){
			return;
		}
		this.move(1, 0);
	}
	
	/**
	 * 响应键盘事件，快速下落，下落到允许下落的最后一个
	 */
	public synchronized void quickDown(){
		if(EnumGameStatus.RUNING.getStatus() != this.gameContext.getGameStatus()){
			return;
		}
		//快速下落
		while(this.move(0, 1));
	}
	
	/**
	 * 是否可以移动
	 * @param moveX 要移动的x坐标
	 * @param moveY 要移动的y坐标
	 * @param points 当前操作的方块组
	 * @return 可移动的新方块组
	 */
	public Point[] canMove(int moveX , int moveY, Point points[]){
		//判断是否可以向4个方向移动，超出边界
		boolean gameMap[][] = this.gameContext.getGameMap();
		Point nps[] = new Point[4];
		boolean flag = true;
		for (int i = 0; i < points.length; i++) {
			Point point = points[i];
			int x = point.x + moveX;
			int y = point.y + moveY;
			//左边、上边、右边、下边 的边界、//判定下一个位置是否存在堆积物
			if(isOverZone(x, y , gameMap)){
				flag = false;
				break;
			}
		}
		if(flag){
			for (int i = 0; i < points.length; i++) {
				Point point = points[i];
				int x = point.x + moveX;
				int y = point.y + moveY;
				nps[i] = new Point(x,y);
			}
			return nps;
		} else {
			return null;
		}
		
	}
	
	/**
	 * 返回最多可移动到的坐标位置（统计总共可以下落多少步骤）
	 * @return 最多可移动到的坐标位置
	 */
	public Point[] getCanMoveDown(){
		Point points[] = this.getGameContext().getActPoint();
		Point nps[] = this.canMove(0, 1, points);
		Point last[] = null;
		if(nps != null){
			last = new Point[points.length];
			while((nps = this.canMove(0, 1, nps)) != null){
				if(nps != null){
					last = nps;
				}
			}
		}
		return last;
	}
	
	/**
	 * 移动方块
	 * @param moveX x坐标
	 * @param moveY y坐标
	 * @return true ？ 可以移动 : 不可移动 
	 */
	public boolean move(int moveX , int moveY){
		Point points[] = this.getGameContext().getActPoint();
		boolean gameMap[][] = this.gameContext.getGameMap();
		//判断是否可以向4个方向移动，超出边界
		boolean flag = true;
		for (int i = 0; i < points.length; i++) {
			Point point = points[i];
			int x = point.x + moveX;
			int y = point.y + moveY;
			//左边、上边、右边、下边 的边界、//判定下一个位置是否存在堆积物
			if(isOverZone(x, y , gameMap)){
				flag = false;
				return flag;
			}
		}
		
		for (Point point : points) {
			point.x += moveX;
			point.y += moveY;
		}
		return flag;
	}
	
	/**
	 * 消行动作，判定整合所有的坐标值全部为true，则消除一行，即所有上方坐标下移
	 * 可消除多个行
	 */
	private void clearLine() {
		List<Integer> clearRowList = new ArrayList<Integer>();
		boolean gameMap[][] = this.gameContext.getGameMap();
		//判断是否可以有消行
		for(int i=0 ; i < gameMap.length; i++){
			for(int j=0; j < gameMap[i].length && !clearRowList.contains(j) ; j++){
				boolean flag = true;
				for(int k=0 ; k < gameMap.length ; k++){
					if(gameMap[k][j] == false){
						flag = false;
						break;
					}
				}
				if(flag){
					clearRowList.add(j);//记录换行的索引
				}
			}
		}
		
		int clearRows = clearRowList.size();
		//消行动作，自己想的实现是将改行所有的数据修改为false，并且将所有的行数据值下标-1
		//消消行的行号前面的所有的障碍物的下标都往下加1
		for (int x = 0; x < clearRows; x++) {
			int clear = clearRowList.get(x);
			for (int i = 0; i < gameMap.length; i++) {
				for (int j = clear; j > 0; j--) {
					gameMap[i][j] = gameMap[i][j - 1];
				}
				//如果某一列（最边上的列下标为0）的数据为true，则在下移一行时需要置为false
				//因为clear的for循环条件为 > 0，忽略了当下标等于0的时候存在数据true的情况
				//此处也可以先判断此格子是否为true，如果为true则标记为false也行
				gameMap[i][0] = false;
			}
		}
		//根据消行的数量累计分数，比如1行10分，2行25分，3行45分，4行80分
		setLevelScore(clearRows);
		
	}

	/**
	 * 根据消除的行数进行分数计算
	 * @param clearRows 本次消除的行数
	 */
	private void setLevelScore(int clearRows) {
		if(clearRows == 0){
			return;
		}
		int newScore = EnumLineScore.getLineScoreByLines(clearRows);
		int score = this.gameContext.getScore();
		this.gameContext.setScore(newScore + score);
	}

	/**
	 * 移动后的坐标判断是否超出了边界，是否已经存在方块
	 * @param x x坐标
	 * @param y y坐标
	 * @param gameMap 游戏地图
	 * @return true ？ 不可移动 ：可以移动
	 */
	private boolean isOverZone(int x, int y , boolean gameMap[][]) {
		 
		return x < MIN_X || y < MIN_Y || x > MAX_X || y > MAX_Y || gameMap[x][y];
	}
	
	/**
	 * 方块旋转
	 * 顺时针：
	 * A.x = O.y + O.x - B.y
	 * A.y = O.y - O.x + B.x
	 */
	private void round() {
		
		int typeCode = this.gameContext.getTypeCode();
		if(typeCode == 4){
			//如果是正方形的方块，则无需旋转
			return;
		}
		
		Point points[] = this.gameContext.getActPoint();
		boolean gameMap[][] = this.gameContext.getGameMap();
		//判断旋转后的方块是否超出地图
		for (int i = 1; i < points.length; i++) {
			Point o = points[0];//中心点
			Point point = points[i];
			int tempX = o.y + o.x - point.y;
			int tempY = o.y - o.x + point.x;
			boolean flag = isOverZone(tempX , tempY , gameMap);
			if(flag){
				return;
			}
		}
		
		for (int i = 1; i < points.length; i++) {
			Point o = points[0];//中心点
			Point point = points[i];
			int tempX = o.y + o.x - point.y;
			int tempY = o.y - o.x + point.x;
			point.x = tempX;
			point.y = tempY;
		}
		
	}

	/**
	 * 设置下一个方块相关的数据
	 * @param newPoint 下一个方块的类型
	 * @param nextNext 存储好下下个方块
	 */
	private void nextBlock(Point newPoint[] , int nextNext) {
		//如果不能移动则出现下一个方块
		int next = this.gameContext.getNext();
		this.gameContext.setActPoint(newPoint);
		this.gameContext.setTypeCode(next);//将下一个设置为当前的方块号
		//下一个的下一个方块
		this.gameContext.setNext(nextNext);
	}
	
	/**
	 * 检查游戏是否结束
	 * @param gameMap 游戏地图
	 * @param newPoints 新方块
	 */
	private void checkGameOver(boolean[][] gameMap, Point[] newPoints) {
		for (Point point : newPoints) {
			if(gameMap[point.x][point.y]){
				this.gameContext.setOver(true);
				this.gameContext.setGameStatus(EnumGameStatus.UN_START.getStatus());
				return;
			}
		}
	}
	
	/**
	 * 重置游戏，游戏结束后，重新构造数据，新游戏
	 */
	public void resetGame() {
		gameContext = new GameContext();
		//设置第一个方块
		gameContext.setTypeCode(new Random().nextInt(7));
		gameContext.setActPoint(this.getPointsByCode(this.getGameContext().getTypeCode()));
		//设置下一个方块
		gameContext.setNext(new Random().nextInt(7));
	}
	
	public GameContext getGameContext() {
		return gameContext;
	}

	public void setGameContext(GameContext gameContext) {
		this.gameContext = gameContext;
	}
}
